package com.ziq.zcamp.util.encrypt;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.Cipher;

/**
 * Author: DongGuangxu
 * Create on: 2016-08-10 18:33
 * Email: guangxu.dong@gmail.com
 * QQ: 121425805
 * PACKAGE_NAME: com.meelive.ingkee.common.util
 * PROJECT_NAME: InkeCommon
 * Description: xxxxxx
 */
public class RSAUtils {

    private static final String RSA = "RSA";

//    public static String PUBLIC_KEY = "WSQpWK0QMCaQCSl3NAOLKAEKK4QXKNMLsAULqANK+MBK0BnUoek9E07NBNoupcz0CScMtWjQrKQ+ygSjmnRANUPfwGeTcyPbgZgbV+tQvHmTm+eNykMX394F9nGtgQ6AMh61RKC4J1jcAsIlPL1s8gmrGGsAoidTl2YP1HKo22HU+mbzVa6FdTVXa2P3x1PduoB3/YjpaPIb98xDygSNKAKL";

    public static String PUBLIC_KEY = "WSQpWK0QMCaQCSl3NAOLKAEKK4QXKNMLsAULqANsEWnOg/kwLblL+ffcQlj4jQ6MCtbxP8QwU1DiXlxt7ZR7qMbzTwBGe0l/YtYMD6BmAo4ffD56iugaKaV+RRNSoRjookhIRTMpSZzLWHgZqTE49jyPQYzQCzdcSiqXbSEpQ1Ie2T/1FBXsDoPYvzFo8eaXiY51KFQBCgGPD+xbzgSNKAKL";

    /**
     * 随机生成RSA密钥对(默认密钥长度为1024)
     *
     * @return
     */
    public static KeyPair generateRSAKeyPair() {
        return generateRSAKeyPair(1024);
    }

    /**
     * 随机生成RSA密钥对
     *
     * @param keyLength 密钥长度，范围：512～2048<br>
     *                  一般1024
     * @return
     */
    public static KeyPair generateRSAKeyPair(int keyLength) {
        try {
            KeyPairGenerator kpg = KeyPairGenerator.getInstance(RSA);
            kpg.initialize(keyLength);
            return kpg.genKeyPair();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 用公钥加密 <br>
     * 每次加密的字节数，不能超过密钥的长度值减去11
     *
     * @param bytes     需加密数据的byte数据
     * @param publicKey 公钥
     * @return 加密后的byte型数据
     */
    public static byte[] encrypt(byte[] bytes, PublicKey publicKey) {
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            // 编码前设定编码方式及密钥
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            // 传入编码数据并返回编码结果
            return cipher.doFinal(bytes);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 用私钥解密
     *
     * @param bytes      经过encryptedData()加密返回的byte数据
     * @param privateKey 私钥
     * @return
     */
    public static byte[] decrypt(byte[] bytes, PrivateKey privateKey) {
        try {
            Cipher cipher = Cipher.getInstance(RSA);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            return cipher.doFinal(bytes);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 通过公钥byte[](publicKey.getEncoded())将公钥还原，适用于RSA算法
     *
     * @param bytes
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static PublicKey getPublicKey(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes);
        KeyFactory keyFactory = KeyFactory.getInstance(RSA);
        PublicKey publicKey = keyFactory.generatePublic(keySpec);
        return publicKey;
    }

    /**
     * 通过私钥byte[]将公钥还原，适用于RSA算法
     *
     * @param bytes
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static PrivateKey getPrivateKey(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);
        KeyFactory keyFactory = KeyFactory.getInstance(RSA);
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
        return privateKey;
    }

    /**
     * 使用N、e值还原公钥
     *
     * @param modulus
     * @param publicExponent
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static PublicKey getPublicKey(String modulus, String publicExponent)
            throws NoSuchAlgorithmException, InvalidKeySpecException {
        BigInteger bigIntModulus = new BigInteger(modulus);
        BigInteger bigIntPrivateExponent = new BigInteger(publicExponent);
        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(bigIntModulus, bigIntPrivateExponent);
        KeyFactory keyFactory = KeyFactory.getInstance(RSA);
        PublicKey publicKey = keyFactory.generatePublic(keySpec);
        return publicKey;
    }

    /**
     * 使用N、d值还原私钥
     *
     * @param modulus
     * @param privateExponent
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static PrivateKey getPrivateKey(String modulus, String privateExponent)
            throws NoSuchAlgorithmException, InvalidKeySpecException {
        BigInteger bigIntModulus = new BigInteger(modulus);
        BigInteger bigIntPrivateExponent = new BigInteger(privateExponent);
        RSAPublicKeySpec keySpec = new RSAPublicKeySpec(bigIntModulus, bigIntPrivateExponent);
        KeyFactory keyFactory = KeyFactory.getInstance(RSA);
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
        return privateKey;
    }

    /**
     * 从字符串中加载公钥
     *
     * @param publicKeyStr 公钥数据字符串
     * @throws Exception 加载公钥时产生的异常
     */
    public static PublicKey getPublicKey(String publicKeyStr) throws Exception {
        try {
            byte[] buffer = Base64Utils.decode(publicKeyStr, Base64Utils.DEFAULT);
            KeyFactory keyFactory = KeyFactory.getInstance(RSA);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
            PublicKey publicKey = keyFactory.generatePublic(keySpec);
            return publicKey;
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此算法");
        } catch (InvalidKeySpecException e) {
            throw new Exception("公钥非法");
        } catch (NullPointerException e) {
            throw new Exception("公钥数据为空");
        }
    }

    /**
     * 从字符串中加载私钥<br>
     * 加载时使用的是PKCS8EncodedKeySpec（PKCS#8编码的Key指令）。
     *
     * @param privateKeyStr
     * @return
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(String privateKeyStr) throws Exception {
        try {
            byte[] buffer = Base64Utils.decode(privateKeyStr, Base64Utils.DEFAULT);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
            KeyFactory keyFactory = KeyFactory.getInstance(RSA);
            PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
            return privateKey;
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此算法");
        } catch (InvalidKeySpecException e) {
            throw new Exception("私钥非法");
        } catch (NullPointerException e) {
            throw new Exception("私钥数据为空");
        }
    }

    /**
     * 从文件中输入流中加载公钥
     *
     * @param in 公钥输入流
     * @throws Exception 加载公钥时产生的异常
     */
    public static PublicKey getPublicKey(InputStream in) throws Exception {
        try {
            return getPublicKey(readKey(in));
        } catch (IOException e) {
            throw new Exception("公钥数据流读取错误");
        } catch (NullPointerException e) {
            throw new Exception("公钥输入流为空");
        }
    }

    /**
     * 从文件中加载私钥
     *
     * @param in 私钥文件名
     * @return 是否成功
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(InputStream in) throws Exception {
        try {
            return getPrivateKey(readKey(in));
        } catch (IOException e) {
            throw new Exception("私钥数据读取错误");
        } catch (NullPointerException e) {
            throw new Exception("私钥输入流为空");
        }
    }

    /**
     * 读取密钥信息
     *
     * @param in
     * @return
     * @throws IOException
     */
    private static String readKey(InputStream in) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(in));
        String readLine = null;
        StringBuilder sb = new StringBuilder();
        while ((readLine = br.readLine()) != null) {
            if (readLine.charAt(0) == '-') {
                continue;
            } else {
                sb.append(readLine);
                sb.append('\r');
            }
        }

        return sb.toString();
    }

    private static final int RSA_1024_ENCYPT_LEN = 117;// 1024位rsa可加密的最大字节长度。

    public static byte[] encryptRSA(byte[] src, String publicKeyString) {
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");// "算法/模式/补码方式"
            // 这里一定是1024位的rsa公钥，服务端保证的。
            PublicKey skeySpec = getPublicKey(publicKeyString);
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
            int srcLen = src.length;
            int srcOffset = 0;
            int encyptLen = RSA_1024_ENCYPT_LEN;
            int outputSize = 0;
            byte[] buffer = new byte[2 * 1024];
            do {
                // 如果末尾不足RSA_1024_ENCYPT_LEN。
                if (srcOffset + RSA_1024_ENCYPT_LEN > srcLen) {
                    encyptLen = srcLen - srcOffset;
                }
                byte[] temp = cipher.doFinal(src, srcOffset, encyptLen);// 单次加密117字节，输出128字节。
                int tempLen = temp.length;
                // 如果buffer不够大，将容量翻倍。
                if (outputSize + tempLen > buffer.length) {
                    byte[] newBuffer = new byte[buffer.length * 2];
                    System.arraycopy(buffer, 0, newBuffer, 0, outputSize);
                    buffer = newBuffer;
                }
                System.arraycopy(temp, 0, buffer, outputSize, tempLen);
                srcOffset += encyptLen;
                outputSize += tempLen;
            } while (srcOffset < srcLen);
            byte[] enBytes = new byte[outputSize];
            System.arraycopy(buffer, 0, enBytes, 0, outputSize);
            return enBytes;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    public static String encryUAC(String publicKey, byte[] data) {
        String str;
        try {
            str =new String(publicKey.getBytes("UTF-8"));
            String pubk = buildNewPubkey(str, false);
            return pubk;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return "";
    }

    public static String encryO(String content){
        String originStr = buildNewPubkey(content,false);
        return buildNewPubkey(originStr,true);
    }

    public static String buildNewPubkey(String publicKey, boolean increment) {
        int pub_key_size = publicKey.length();
        int size = 0;
        int padding = 0;
        int pad_len = 0;//pub_key_size % 64 == 0 ? 0 : 1;

        pad_len += 0;//pub_key_size / 64;

        int arr_len = pub_key_size + pad_len;


        char[] charStr = new char[arr_len];
        char[] publicKeyChar = publicKey.toCharArray();
        for (int i = 0; i < pub_key_size; i++) {
            char c = publicKeyChar[i];
            char new_c = transformChar(c, increment);
            charStr[i + padding] = new_c;
            size++;
        }

        String strChar = String.valueOf(charStr);
//
        strChar = strChar.substring(0, pub_key_size + pad_len);

        return strChar;
    }

    private static char transformChar(char inChar, boolean increment) {
        int inc;

        if (increment == true) {
            inc = 10;
        } else {
            inc = -10;
        }

        if (inChar >= 'a' && inChar <= 'z') {
            return (char) ('a' + (inChar - 'a' + inc + 26) % 26);
        }
        if (inChar >= 'A' && inChar <= 'Z') {
            return (char) ('A' + (inChar - 'A' + inc + 26) % 26);
        }
        return inChar;
    }
}
